#include "configparser.h"
#include "tinyxml2.h"
#include <vector>
#include <regex>
#include <sstream>

using namespace std;
using namespace Util;

static void stringSplit(vector<string>& Result, string& Input, const char* Regex)
{
    int pos = 0;
    int npos = 0;
    int regexlen = strlen(Regex);
    while ((npos = Input.find(Regex, pos)) != -1)
    {
        string tmp = Input.substr(pos, npos - pos);
        Result.push_back(tmp);
        pos = npos + regexlen;
    }
    Result.push_back(Input.substr(pos, Input.length() - pos));
}
static uint32_t hexstr2dec(const char* hex)
{
    uint32_t ret = 0;
    int len = strlen(hex);
    if (len > 10)
        return 0xffffffff;
    if (hex[0] != '#' || hex[1] != 'x')
        return 0xffffffff;
    for (int i = 2; i < len; i++)
    {
        uint32_t mid;
        if (hex[i] >= '0' && hex[i] <= '9')
            mid = hex[i] - '0';
        else if (hex[i] >= 'a' && hex[i] <= 'f')
            mid = hex[i] - 'a' + 10;
        else if (hex[i] >= 'A' && hex[i] <= 'F')
            mid = hex[i] - 'A' + 10;
        else
            return 0xffffffff;
        mid <<= ((len - i - 1) << 2);
        ret |= mid;
    }
    return ret;
}
class Util::ConfigParserImpl
{
public:
    ConfigParserImpl(const char* path)
    {
        _valid = false;
        root = nullptr;
        tinyxml2::XMLError ret = doc.LoadFile(path);
        if (ret == tinyxml2::XML_SUCCESS)
        {
            _valid = true;
            root = doc.RootElement();
        }
    }
    ~ConfigParserImpl() {}
    int create(const char* path, string root_name)
    {
        if (root_name == "")
            root_name = "Root";
        const char* declaration = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
        doc.Parse(declaration);
        root = doc.NewElement(root_name.c_str());
        if (!root)
            return -1;
        doc.InsertEndChild(root);
        doc.SaveFile(path);
        _valid = true;
        return 0;
    }
    void clear() { doc.Clear(); }
    tinyxml2::XMLElement* add_ele(std::string exp, bool is_attr)
    {
        if (!is_valid())
            return nullptr;
        vector<string> ele_str;
        stringSplit(ele_str, exp, ".");
        tinyxml2::XMLElement* ele = root;
        for (uint32_t i = 0; i < ele_str.size(); i++)
        {
            std::smatch m;
            //提取名称
            string name;
            int index = 0;
            if (!std::regex_match(ele_str[i], m, std::regex("(.*)\\[(\\d+)\\]")))
            {
                name = ele_str[i];
            }
            else
            {
                name = m[0];
                if (m.size() >= 2)
                    name = m[1];
                if (m.size() == 3)
                    index = std::stoi(m[2]);
            }

            //寻找ele
            if (i + 1 == ele_str.size())  //匹配到索引项
            {
                if (is_attr)
                {
                    if (ele->Attribute(name.c_str()) == NULL)  //无该attr则创建
                    {
                        ele->SetAttribute(name.c_str(), "");
                    }
                    return ele;
                }
            }
            tinyxml2::XMLElement* temp = ele->FirstChildElement(name.c_str());
            if (!temp)  //无该ele则创建
            {
                tinyxml2::XMLElement* new_ele = doc.NewElement(name.c_str());
                ele->InsertEndChild(new_ele);
                temp = new_ele;
            }
            for (int j = 0; j < index; j++)
            {
                temp = temp->NextSiblingElement(name.c_str());
                if (!temp)
                {
                    tinyxml2::XMLElement* new_ele = doc.NewElement(name.c_str());
                    ele->InsertEndChild(new_ele);
                    temp = new_ele;
                }
            }
            ele = temp;
        }
        return ele;
    }
    tinyxml2::XMLElement* parser_exp(std::string exp, bool is_attr, string* attr_name = nullptr)
    {
        if (!is_valid())
            return nullptr;
        vector<string> ele_str;
        stringSplit(ele_str, exp, ".");
        tinyxml2::XMLElement* ele = root;
        for (uint32_t i = 0; i < ele_str.size(); i++)
        {
            std::smatch m;
            string name;
            int index = 0;
            if (!std::regex_match(ele_str[i], m, std::regex("(.*)\\[(\\d+)\\]")))
            {
                name = ele_str[i];
            }
            else
            {
                name = m[0];
                if (m.size() >= 2)
                    name = m[1];
                if (m.size() == 3)
                    index = std::stoi(m[2]);
            }

            if (i + 1 == ele_str.size())
            {
                if (is_attr)
                {
                    if (ele->Attribute(name.c_str()) == NULL)
                        return nullptr;
                    if (attr_name)  // FIXME
                    {
                        *attr_name = name;
                        return ele;
                    }
                    else
                        return nullptr;
                }
            }
            tinyxml2::XMLElement* temp = ele->FirstChildElement(name.c_str());
            if (!temp)
                return nullptr;

            for (int j = 0; j < index; j++)
            {
                temp = temp->NextSiblingElement(name.c_str());
                if (!temp)
                    return nullptr;
            }
            ele = temp;
        }
        return ele;
    }
    tinyxml2::XMLDocument doc;
    tinyxml2::XMLElement* root;
    inline bool is_valid() const { return _valid; }

private:
    bool _valid;
};
ConfigParser::ConfigParser(const char* path)
{
    _impl = new ConfigParserImpl(path);
    _path = path;
}
ConfigParser::~ConfigParser() { delete _impl; }
int ConfigParser::create_file(string root_name) { return _impl->create(_path.c_str(), root_name); }
int ConfigParser::clear_file()
{
    if (_impl->is_valid())
    {
        _impl->clear();
        return 0;
    }
    return -1;
}
int ConfigParser::get(std::string exp, int& val, bool is_attr)
{
    string str;
    int ret = get(exp, str, is_attr);
    if (ret)
        return ret;
    if (*str.begin() == '#')
    {
        val = hexstr2dec(str.c_str());
    }
    else
    {
        val = atoi(str.c_str());
    }
    return 0;
}
int ConfigParser::get(std::string exp, std::vector<int>& val, bool is_attr)
{
    vector<string> str;
    int ret = get(exp, str, is_attr);
    if (ret)
        return ret;
    for (uint32_t i = 0; i < str.size(); i++) val.push_back(std::stoi(str[i]));
    return 0;
}
int ConfigParser::set(std::string exp, int val, bool is_attr)
{
    string valstr = to_string(val);
    return set(exp, valstr, is_attr);
}
int ConfigParser::add(std::string exp, int val, bool is_attr)
{
    string valstr = to_string(val);
    return add(exp, valstr, is_attr);
}
int ConfigParser::get(std::string exp, std::string& val, bool is_attr)
{
    if (!_impl->is_valid())
        return -1;
    vector<string> ele_str;
    stringSplit(ele_str, exp, ".");
    tinyxml2::XMLElement* ele = _impl->root;
    for (uint32_t i = 0; i < ele_str.size(); i++)
    {
        std::smatch m;
        string name;
        int index = 0;
        if (!std::regex_match(ele_str[i], m, std::regex("(.*)\\[(\\d+)\\]")))
        {
            name = ele_str[i];
        }
        else
        {
            name = m[0];
            if (m.size() >= 2)
                name = m[1];
            if (m.size() == 3)
                index = std::stoi(m[2]);
        }

        if (i + 1 == ele_str.size())
        {
            if (is_attr)
            {
                const char* str = nullptr;
                if (ele->QueryStringAttribute(name.c_str(), &str) != tinyxml2::XML_SUCCESS)
                    return -1;
                val = string(str);
                return 0;
            }
        }
        tinyxml2::XMLElement* temp = ele->FirstChildElement(name.c_str());
        if (!temp)
            return -1;

        for (int j = 0; j < index; j++)
        {
            temp = temp->NextSiblingElement(name.c_str());
            if (!temp)
                return -1;
        }
        ele = temp;
    }
    val = ele->GetText();
    return 0;
}
int ConfigParser::set(std::string exp, std::string val, bool is_attr)
{
    string attrname;
    auto ele = _impl->parser_exp(exp, is_attr, &attrname);
    if (!ele)
        return -1;
    if (is_attr)
    {
        ele->SetAttribute(attrname.c_str(), val.c_str());
    }
    else
    {
        ele->SetText(val.c_str());
    }
    _impl->doc.SaveFile(_path.c_str());
    return 0;
}
int ConfigParser::add(std::string exp, std::string val, bool is_attr)
{
    string attrname;
    auto ele = _impl->parser_exp(exp, is_attr, &attrname);
    if (ele)
    {
        return set(exp, val, is_attr);
    }
    if (!_impl->add_ele(exp, is_attr))
        return -1;

    return set(exp, val, is_attr);
}
int ConfigParser::get(std::string exp, std::vector<std::string>& val, bool is_attr)
{
    string str;
    int ret = get(exp, str, is_attr);
    if (ret)
        return ret;
    stringSplit(val, str, " ");
    return 0;
}
int ConfigParser::set(std::string exp, std::vector<std::string> val)
{
    string strval;
    for (size_t i = 0; i < val.size(); i++) { strval += val[i] + " "; }
    strval.pop_back();  //去除最后的空格
    return set(exp, strval, false);
}
int ConfigParser::get(std::string exp, double& val, bool is_attr)
{
    string str;
    int ret = get(exp, str, is_attr);
    if (ret)
        return ret;
    val = std::stod(str);
    return 0;
}
int ConfigParser::add(std::string exp, double val, bool is_attr)
{
    string valstr = to_string(val);
    return add(exp, valstr, is_attr);
}
int ConfigParser::set(std::string exp, double val, bool is_attr)
{
    string valstr = to_string(val);
    return set(exp, valstr, is_attr);
}
int ConfigParser::get(std::string exp, std::vector<double>& val)
{
    vector<string> str;
    int ret = get(exp, str);
    if (ret)
        return ret;
    for (uint32_t i = 0; i < str.size(); i++) val.push_back(std::stod(str[i]));
    return 0;
}
int ConfigParser::set(std::string exp, std::vector<double> val)
{
    string strval;
    for (size_t i = 0; i < val.size(); i++) { strval += to_string(val[i]) + " "; }
    strval.pop_back();  //去除最后的空格
    return set(exp, strval, false);
}
static bool is_number(const string& str)
{
    istringstream sin(str);
    double test;
    return sin >> test && sin.eof();
}
static bool str2bool(const string& str)
{
    bool val;
    if (is_number(str))
    {
        if (std::stod(str) == 0)
            val = false;
        else
            val = true;
    }
    else
    {
        if (str == "true")
            val = true;
        else
            val = false;
    }
    return val;
}

int ConfigParser::get(std::string exp, bool& val, bool is_attr)
{
    string str;
    int ret = get(exp, str, is_attr);
    if (ret)
        return ret;
    val = str2bool(str);
    return 0;
}
int ConfigParser::set(std::string exp, bool val, bool is_attr) { return set(exp, val == true ? 1 : 0, is_attr); }
int ConfigParser::get(std::string exp, std::vector<bool>& val, bool is_attr)
{
    vector<string> str;
    int ret = get(exp, str, is_attr);
    if (ret)
        return ret;
    for (uint32_t i = 0; i < str.size(); i++) { val.push_back(str2bool(str[i])); }
    return 0;
}
int ConfigParser::set(std::string exp, std::vector<bool> val)
{
    string strval;
    for (size_t i = 0; i < val.size(); i++) { strval += to_string(val[i] ? 1 : 0) + " "; }
    strval.pop_back();  //去除最后的空格
    return set(exp, strval, false);
}
bool ConfigParser::is_valid()
{
    if (!_impl)
        return false;
    return _impl->is_valid();
}